﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net
{
    using System.Collections.Generic;

    /// <summary>Represents a command to be sent to a server.</summary>
    /// <typeparam name="TClient">The type of the client the command is for.</typeparam>
    /// <remarks>Concrete commands should virtually never implement this interface directly, but derive from
    /// protocol-specific classes, like e.g. <see cref="Nntp.SingleLineCommand{T}"/> or
    /// <see cref="Nntp.NonPipelinableCommand{T}"/>.</remarks>
    /// <threadsafety static="true" instance="false"/>
    public interface ICommand<TClient> where TClient : IClient<TClient>
    {
        /// <summary>Gets whether the command is pipelinable, see
        /// <a href="http://www.ietf.org/rfc/rfc3977.txt">RFC 3977</a> 3.5.</summary>
        /// <remarks>Note that the concept of pipelining is universal and does not just apply to the NNTP protocol.
        /// </remarks>
        bool IsPipelinable { get; }

        /// <summary>Sends the bytes that constitute the first stage of the command.</summary>
        /// <param name="createCommandStreamCallback">The method that creates a stream, through which a first stage can
        /// be written, see <see cref="CreateCommandStreamCallback"/>.</param>
        /// <remarks>Implementations must not swallow any exceptions thrown from
        /// <paramref name="createCommandStreamCallback"/> or <see cref="CommandStream"/> methods.</remarks>
        void SendCommand(CreateCommandStreamCallback createCommandStreamCallback);

        /// <summary>Receives the response to the first stage of the command and then possibly handles the second and
        /// subsequent stages.</summary>
        /// <remarks>Implementations must not swallow any exceptions thrown from
        /// <paramref name="createResponseStreamCallback"/>, <paramref name="createCommandStreamCallback"/>,
        /// <see cref="ResponseStream"/> methods or <see cref="CommandStream"/> methods.</remarks>
        IResponse ReceiveResponse(
            CreateResponseStreamCallback createResponseStreamCallback,
            CreateCommandStreamCallback createCommandStreamCallback);

        /// <summary>Returns a value indicating whether the response starting with
        /// <paramref name="initialResponseLineWords"/> is multi-line or not.</summary>
        /// <param name="initialResponseLineWords">The words of the initial line of the response.</param>
        /// <remarks><paramref name="initialResponseLineWords"/> contains as many elements as were specified during the
        /// last call to a <see cref="CreateResponseStreamCallback"/> delegate.</remarks>
        bool IsMultilineResponse(IList<string> initialResponseLineWords);

        /// <summary>Throws an appropriate exception if <paramref name="initialResponseLineWords"/> indicates that the
        /// associated command failed.</summary>
        /// <exception cref="ClientBaseException">The command associated with the response failed. The type of the
        /// exception indicates the cause of the error (<see cref="ClientBaseException"/> is abstract so only subclass
        /// objects can be thrown).</exception>
        /// <remarks>See implementing classes for more information about the exact <see cref="ClientBaseException"/>
        /// subtypes thrown.</remarks>
        void ThrowOnError(IList<string> initialResponseLineWords);
    }
}
